package com.bedrock.project.inside.service.impl;

import com.alibaba.fastjson.JSON;
import com.bedrock.common.constant.ProjectConstants;
import com.bedrock.common.constant.RecruitConstants;
import com.bedrock.common.constant.WorkHourConstants;
import com.bedrock.common.utils.*;
import com.bedrock.framework.config.ServerConfig;
import com.bedrock.framework.security.LoginUser;
import com.bedrock.framework.web.domain.AjaxResult;
import com.bedrock.project.inside.entity.WorkHour;
import com.bedrock.project.inside.entity.WorkHourWeek;
import com.bedrock.project.inside.mapper.WorkHourMapper;
import com.bedrock.project.inside.mapper.WorkHourWeekMapper;
import com.bedrock.project.inside.service.WorkHourService;
import com.bedrock.project.inside.vo.request.MailVo;
import com.bedrock.project.inside.vo.response.WorkHourSumVo;
import com.bedrock.project.system.domain.SysRole;
import com.bedrock.project.system.service.ISysConfigService;
import com.bedrock.project.system.service.ISysUserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工时填报Service业务层处理
 */
@Service
public class WorkHourServiceImpl implements WorkHourService {

    protected final Logger logger = LoggerFactory.getLogger(WorkHourServiceImpl.class);

    @Autowired
    private WorkHourMapper workHourMapper;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private WorkHourWeekMapper workHourWeekMapper;

    @Autowired
    private ServerConfig serverConfig;

    /**
     * 查询工时填报
     */
    @Override
    public Map selectWorkHourById(Long workHourId) {
        return workHourMapper.selectWorkHourById(workHourId);
    }

    /**
     * 查询工时填报列表 ,需要自行分页
     */
    @Override
    public List<WorkHour> selectWorkHourList(WorkHour workHour) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long userId = loginUser.getUser().getUserId();
        workHour.setUserId(userId);
        List<WorkHour> selfList = workHourMapper.selectWorkHourList(workHour);
        workHour.setUserId(null);
        List<SysRole> roleList = loginUser.getUser().getRoles();
        logger.info("用户的权限key列表为{}", JSON.toJSONString(roleList));
        // 权限校验
        List<String> roleKeyList = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(roleList)){
            for (SysRole sysRole : roleList) {
                roleKeyList.add(sysRole.getRoleKey());
            }
        }else{
            return selfList;
        }
        //部门主管或admin查看所有工时
        if(roleKeyList.contains(ProjectConstants.ROLE_KEY_PMO)||roleKeyList.contains(ProjectConstants.ROLE_KEY_ADMIN)){
        }
        //项目经理查看自己负责的项目工时
        else if(roleKeyList.contains(ProjectConstants.ROLE_KEY_PM)){
            workHour.setPrincipalId(userId);
        }
        List<WorkHour> hourList = workHourMapper.selectWorkHourList(workHour);
        if(CollectionUtils.isNotEmpty(hourList)){
            hourList.removeAll(selfList);
            hourList.addAll(selfList);
            hourList = hourList.stream().sorted(Comparator.comparing(WorkHour::getStartDate).reversed()).collect(Collectors.toList());
            return hourList;
        }
        return selfList;
    }


    /**
     * 新增工时填报
     */
    @Override
    public AjaxResult insertWorkHour(WorkHour workHour) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        workHour.setUserId(loginUser.getUser().getUserId());
        workHour.setStatus(WorkHourConstants.WORK_HOUR_OK);
        workHour.setWorkStatus(WorkHourConstants.WORK_HOUR_SEND_OK);
        workHour.setCreateTime(DateUtils.getNowDate());
        workHour.setUpdateTime(DateUtils.getNowDate());
        int i = workHourMapper.insertWorkHour(workHour);
        return i > 0 ? AjaxResult.success() : AjaxResult.error("添加工时失败");
    }

    /**
     * 修改工时填报
     *
     * @param workHour 工时填报
     * @return 结果
     */
    @Override
    public int updateWorkHour(WorkHour workHour) {
        workHour.setUpdateTime(DateUtils.getNowDate());
        return workHourMapper.updateWorkHour(workHour);
    }

    /**
     * 批量删除工时填报
     *
     * @param workHourIds 需要删除的工时填报ID
     * @return 结果
     */
    @Override
    public int deleteWorkHourByIds(Long[] workHourIds) {
        return workHourMapper.deleteWorkHourByIds(workHourIds);
    }

    /**
     * 删除工时填报信息
     *
     * @param workHourId 工时填报ID
     * @return 结果
     */
    @Override
    public int deleteWorkHourById(Long workHourId) {
        return workHourMapper.deleteWorkHourById(workHourId);
    }


    @Override
    public List<Map> getProjectList() {
        return workHourMapper.getProjectList();
    }


    @Override
    public Map getHourDictData() {
        HashMap<String, Object> result = new HashMap<>();
        List<Map> workhourStage = workHourMapper.getWorkhourStage();
        List<Map> workhourUsertype = workHourMapper.getWorkhourUsertype();
        result.put("workhourStage", workhourStage);
        result.put("workhourUsertype", workhourUsertype);
        return result;
    }


    /**
     * 工时周报列表
     */
    @Override
    public AjaxResult sendWeekReport() {
        List<Map> weekReportList = workHourMapper.findWeekReport();
        return AjaxResult.success(weekReportList);
    }

    /**
     * 发送邮件
     */
    @Override
    public void sendMailWeekReport() {
        sendWeekReportSum(null,null);
        sendWeekReportToPrincipal(null,null);
        sendWeekReportToSalesman(null,null);
    }


    /**
     * 汇总主管接收(PMO)
     */
    private void sendWeekReportSum(List<WorkHourSumVo> warnList,String costUp) {
        List<WorkHourSumVo> sumList = new ArrayList<>();
        boolean isSendWarnEmail=false;
        if(CollectionUtils.isEmpty(warnList)){
            sumList = workHourMapper.sumWorkHour();
        }else {
            isSendWarnEmail =true;
            sumList =warnList;
        }
        MailVo mailVo = new MailVo();
        String toMails = configService.selectConfigByKey(RecruitConstants.WORK_HOUR_WEEKLY_TOMAILS);
        StringBuilder content = new StringBuilder(ProjectConstants.HTML_HEAD);
        content.append("<div class='box-title'><h3>");
        if(isSendWarnEmail){
            content.append("项目超出预定值预警表 "+EmailUtils.getCurrentDay()+"</h3></div>");
        }else {
            content.append("总工时周报 ");
            content.append(EmailUtils.getOneWeekDay());
            content.append("</h3><p class='p1'>");
            content.append(EmailUtils.getCurrentWeek());
            content.append("</p></div>");
        }
        content.append("<thead><tr><th width='100'>");
        content.append("项目名称");
        content.append("</th><th width='100'>");
        content.append("销售");
        content.append("</th><th width='100'>");
        content.append("项目经理");
        content.append("</th><th width='100'>");
        content.append("总人数");
        content.append("</th><th width='100'>");
        content.append("总工时");
        content.append("</th><th width='100'>");
        content.append("销售总成本(万元)");
        content.append("</th><th width='100'>");
        content.append("研发总成本(万元)");
        content.append("</th></tr></thead><tbody>");
        int developerTotal = 0;
        float workHourTotal = 0;
        BigDecimal saleTotal = BigDecimal.ZERO;
        BigDecimal innerCost = BigDecimal.ZERO;
        if(CollectionUtils.isNotEmpty(sumList)) {
            for (WorkHourSumVo weekReport : sumList) {
                content.append("<tr>");
                content.append("<td>" + weekReport.getProjectName() + "</td>");
                content.append("<td>" + weekReport.getSalemanName() + "</td>");
                content.append("<td>" + weekReport.getPrincipalName() + "</td>");
                content.append("<td>" + weekReport.getDeveloperTotal() + "</td>");
                content.append("<td>" + weekReport.getWorkHourTotal() + "</td>");
                content.append("<td>" + weekReport.getSaleTotal() + "</td>");
                content.append("<td>" + weekReport.getInnerCost().divide(BigDecimal.valueOf(10000L)) + "</td>");
                content.append("</tr>");
                developerTotal += weekReport.getDeveloperTotal();
                workHourTotal += weekReport.getWorkHourTotal();
                saleTotal=saleTotal.add(weekReport.getSaleTotal());
                innerCost=innerCost.add(weekReport.getInnerCost().divide(BigDecimal.valueOf(10000L)));
            }
        }
        if(!isSendWarnEmail) {
            content.append("<tr>");
            content.append("<td>" + "合计" + "</td>");
            content.append("<td>" + "" + "</td>");
            content.append("<td>" + "" + "</td>");
            content.append("<td>" + developerTotal + "</td>");
            content.append("<td>" + workHourTotal + "</td>");
            content.append("<td>" + saleTotal + "</td>");
            content.append("<td>" + innerCost + "</td>");
            content.append("</tr>");
        }
        content.append("</tbody>");
        content.append("</table>");
        if(isSendWarnEmail){
            content.append("<div class='box-fool' style='width:742px'><p>");
            content.append("当前未立项项目的预警值为 :"+costUp+",修改预警值请在 系统管理>参数设置修改参数键名为'project.cost.up'的值");
            content.append("</p></div>");
        }
        content.append("</body></html>");

        mailVo.setToMails(toMails);
        mailVo.setContent(content.toString());
        if(isSendWarnEmail){
            mailVo.setSubject("项目预警日报邮件");
        }else {
            mailVo.setSubject("总工时周报邮件");
        }
        try {
            EmailUtils.sendMail(mailVo);
            logger.info("总工时邮件发送成功,收件人{}",mailVo.getToMails());
        } catch (Exception e) {
            logger.error("发送邮件给部门主管异常{},时间{}",e.getMessage(),DateUtils.getNowDate());
        }
    }


    /**
     * 负责人接收（PM）
     */
    private void sendWeekReportToPrincipal(List<WorkHourSumVo> warnList,String costUp) {
        List<WorkHourSumVo> list = new ArrayList<>();
        boolean isSendWarnEmail = false;
        if(CollectionUtils.isEmpty(warnList)){
            list = workHourMapper.sumWorkHour();
        }else {
            list =warnList;
            isSendWarnEmail =true;
        }
        Map<Long, List<WorkHourSumVo>> principalMap= list.stream().collect(Collectors.groupingBy(WorkHourSumVo::getPrincipalId));
        for(Map.Entry<Long, List<WorkHourSumVo>> entry:principalMap.entrySet()) {
            List<WorkHourSumVo> sumList =entry.getValue();
            MailVo mailVo = new MailVo();
            String toMails = sysUserService.selectUserById(entry.getKey()).getEmail();
            StringBuilder content = new StringBuilder(ProjectConstants.HTML_HEAD);
            content.append("<div class='box-title'><h3>");
            if(isSendWarnEmail){
                content.append("项目超出预定值预警表 "+EmailUtils.getCurrentDay()+"</h3></div>");
            }else {
                content.append("研发工时周报 ");
                content.append(EmailUtils.getOneWeekDay());
                content.append("</h3><p class='p1'>");
                content.append(EmailUtils.getCurrentWeek());
                content.append("</p></div>");
            }
            content.append("<thead><tr><th width='100'>");
            content.append("项目名称");
            content.append("</th><th width='100'>");
            content.append("销售");
            content.append("</th><th width='100'>");
            content.append("总人数");
            content.append("</th><th width='100'>");
            content.append("总工时");
            content.append("</th><th width='100'>");
            content.append("销售总成本(万元)");
            content.append("</th><th width='100'>");
            content.append("研发总成本(万元)");
            content.append("</th></tr></thead><tbody>");
            int developerTotal = 0;
            float workHourTotal = 0;
            BigDecimal saleTotal = BigDecimal.ZERO;
            BigDecimal innerCost = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(sumList)) {
                for (WorkHourSumVo weekReport : sumList) {
                    content.append("<tr>");
                    content.append("<td>" + weekReport.getProjectName() + "</td>");
                    content.append("<td>" + weekReport.getSalemanName() + "</td>");
                    content.append("<td>" + weekReport.getDeveloperTotal() + "</td>");
                    content.append("<td>" + weekReport.getWorkHourTotal() + "</td>");
                    content.append("<td>" + weekReport.getSaleTotal() + "</td>");
                    content.append("<td>" + weekReport.getInnerCost().divide(BigDecimal.valueOf(10000L)) + "</td>");
                    content.append("</tr>");
                    developerTotal += weekReport.getDeveloperTotal();
                    workHourTotal += weekReport.getWorkHourTotal();
                    saleTotal = saleTotal.add(weekReport.getSaleTotal());
                    innerCost = innerCost.add(weekReport.getInnerCost().divide(BigDecimal.valueOf(10000L)));
                }
            }
            if(!isSendWarnEmail) {
                content.append("<tr>");
                content.append("<td>" + "合计" + "</td>");
                content.append("<td>" + "" + "</td>");
                content.append("<td>" + developerTotal + "</td>");
                content.append("<td>" + workHourTotal + "</td>");
                content.append("<td>" + saleTotal + "</td>");
                content.append("<td>" + innerCost + "</td>");
                content.append("</tr>");
            }
            content.append("</tbody>");
            content.append("</table>");
            if(isSendWarnEmail){
                content.append("<div class='box-fool' style='width:636px'><p>");
                content.append("当前未立项项目的预警值为 :"+costUp+",修改预警值请在 系统管理>参数设置修改参数键名为'project.cost.up'的值");
                content.append("</p></div>");
            }
            content.append("</body></html>");

            mailVo.setToMails(toMails);
            mailVo.setContent(content.toString());
            if(isSendWarnEmail){
                mailVo.setSubject("研发预警日报邮件");
            }else {
                mailVo.setSubject("研发工时周报邮件");
            }
            try {
                EmailUtils.sendMail(mailVo);
                logger.info("研发工时邮件发送成功,收件人{}",mailVo.getToMails());
            } catch (Exception e) {
                logger.error("发送邮件给项目经理异常{},时间{}",e.getMessage(),DateUtils.getNowDate());
            }
        }
    }


    /**
     * 销售接收(salesman)
     */
    private void sendWeekReportToSalesman(List<WorkHourSumVo> warnList,String costUp) {
        List<WorkHourSumVo> list = new ArrayList<>();
        boolean isSendWarnEmail =false;
        if(CollectionUtils.isEmpty(warnList)){
            list = workHourMapper.sumWorkHour();
        }else {
            list =warnList;
            isSendWarnEmail =true;
        }
        Map<Long, List<WorkHourSumVo>> principalMap= list.stream().collect(Collectors.groupingBy(WorkHourSumVo::getSalemanId));
        String url = serverConfig.getUrl() + "/workhour/v1/confirmWorkHour";
        List<WorkHourWeek> workHourWeekList = new ArrayList<WorkHourWeek>();
        for(Map.Entry<Long, List<WorkHourSumVo>> entry:principalMap.entrySet()) {
            List<WorkHourSumVo> sumList =entry.getValue();
            MailVo mailVo = new MailVo();
            String toMails = sysUserService.selectUserById(entry.getKey()).getEmail();
            StringBuilder content = new StringBuilder(ProjectConstants.HTML_HEAD);
            content.append("<div class='box-title'><h3>");
            if(isSendWarnEmail){
                content.append("项目超出预定值预警表 "+EmailUtils.getCurrentDay()+"</h3></div>");
            }else {
                content.append("销售工时周报 ");
                content.append(EmailUtils.getOneWeekDay());
                content.append("</h3><p class='p1'>");
                content.append(EmailUtils.getCurrentWeek());
                content.append("</p></div>");
            }
            content.append("<thead><tr><th width='100'>");
            content.append("项目名称");
            content.append("</th><th width='100'>");
            content.append("项目经理");
            content.append("</th><th width='100'>");
            content.append("总人数");
            content.append("</th><th width='100'>");
            content.append("总工时");
            content.append("</th><th width='100'>");
            content.append("总成本(万元)");
            content.append("</th></tr></thead><tbody>");
            int developerTotal = 0;
            float workHourTotal = 0;
            BigDecimal outingCost = BigDecimal.ZERO;
            // 销售下的邮件工时Id
            Long emailHourId = IdWord.getId();
            if (CollectionUtils.isNotEmpty(sumList)) {
                for (WorkHourSumVo weekReport : sumList) {
                    content.append("<tr>");
                    content.append("<td>" + weekReport.getProjectName() + "</td>");
                    content.append("<td>" + weekReport.getPrincipalName() + "</td>");
                    content.append("<td>" + weekReport.getDeveloperTotal() + "</td>");
                    content.append("<td>" + weekReport.getWorkHourTotal() + "</td>");
                    content.append("<td>" + weekReport.getInnerCost().divide(BigDecimal.valueOf(10000L)) + "</td>");
                    content.append("</tr>");
                    developerTotal += weekReport.getDeveloperTotal();
                    workHourTotal += weekReport.getWorkHourTotal();
                    outingCost = outingCost.add(weekReport.getOutingCost().divide(BigDecimal.valueOf(10000L)));
                    if(!isSendWarnEmail) {
                        IdWord idWord = new IdWord(1, 1);
                        workHourWeekList.add(WorkHourWeek.builder()
                                .id(idWord.nextId())
                                .confirmId(emailHourId)
                                .userId(weekReport.getSalemanId())
                                .projectId(weekReport.getProjectId())
                                .projectName(weekReport.getProjectName())
                                .principalName(weekReport.getPrincipalName())
                                .developerTotal(weekReport.getDeveloperTotal())
                                .workHourTotal(weekReport.getWorkHourTotal())
                                .costOuting(weekReport.getOutingCost())
                                .confirmStartDate(LocalDate.now().with(DayOfWeek.MONDAY))
                                .confirmEndDate(LocalDate.now())
                                .build());
                    }
                }
            }
            if(!isSendWarnEmail) {
                content.append("<tr>");
                content.append("<td>" + "合计" + "</td>");
                content.append("<td>" + "" + "</td>");
                content.append("<td>" + developerTotal + "</td>");
                content.append("<td>" + workHourTotal + "</td>");
                content.append("<td>" + outingCost + "</td>");
                content.append("</tr>");
            }
            content.append("</tbody>");
            content.append("</table>");
            if(isSendWarnEmail){
                content.append("<div class='box-fool' style='width:530px'><p>");
                content.append("当前未立项项目的预警值为 :"+costUp+",修改预警值请在 系统管理>参数设置修改参数键名为'project.cost.up'的值");
                content.append("</p></div>");
            }else{
                content.append("<div class='box-fool' style='width:530px'><p>");
                content.append("<a href=");
                content.append(url + "?confirmId=").append(emailHourId).append("&userId=").append(entry.getKey());
                content.append(">确认工时</a>");
                content.append("</p></div>");
            }
            content.append("</body></html>");

            mailVo.setToMails(toMails);
            mailVo.setContent(content.toString());
            if(isSendWarnEmail){
                mailVo.setSubject("销售预警日报邮件");
            }else {
                mailVo.setSubject("销售工时周报邮件");
            }
            try {
                EmailUtils.sendMail(mailVo);
                logger.info("销售工时邮件发送成功,收件人{}",mailVo.getToMails());
            } catch (Exception e) {
                logger.error("发送邮件给销售异常{},时间{}",e.getMessage(),DateUtils.getNowDate());
            }
        }
        //新增项目工时周报
        if(CollectionUtils.isNotEmpty(workHourWeekList)){
            for (WorkHourWeek workHourWeek : workHourWeekList){
                workHourWeekMapper.insertWorkHourWeek(workHourWeek);
            }
        }
    }

    /**
     * 发送预警邮件, 当未立项项目投入超8w时候发送预警邮件,频率每天
     */
    @Override
    public void sendWeekReportWarn() {
        List<WorkHourSumVo> sumList = workHourMapper.sumWorkHour();
        List<WorkHourSumVo> warnList = new ArrayList<>();
        String projectCostUp = configService.selectConfigByKey(RecruitConstants.PROJECT_COST_UP);
        if(StringUtils.isEmpty(projectCostUp)){
            logger.error("需要配置项目预警金额");
            return;
        }
        BigDecimal costUp = null;
        try {
            costUp = new BigDecimal(projectCostUp);
        } catch (Exception e) {
            logger.error("配置的项目预警金额需要是数字");
            return;
        }
        if(CollectionUtils.isNotEmpty(sumList)){
            for (WorkHourSumVo workHourSumVo : sumList) {
                //未立项项目且投入大于8w
                if((ProjectConstants.PMP_PROJECT_STATUS_UNDERWAY.equals(workHourSumVo.getProjectStatus())||
                ProjectConstants.PMP_PROJECT_STATUS_TENDER.equals(workHourSumVo.getProjectStatus()))&&
                workHourSumVo.getInnerCost().compareTo(costUp)==1){
                    warnList.add(workHourSumVo);
                }
            }
        }
        if(CollectionUtils.isEmpty(warnList)){
            logger.info("定时检查项目预警无超过预警的项目,时间{}",DateUtils.getNowDate());
            return;
        }
        sendWeekReportSum(warnList,costUp.toString());
        sendWeekReportToPrincipal(warnList,costUp.toString());
        sendWeekReportToSalesman(warnList,costUp.toString());
    }

    @Override
    public boolean checkWorkHourByProject(Long projectId){
        int count = workHourMapper.countByProjectId(projectId);
        if(count>0){
            return true;
        }
        return false;
    }

    @Override
    public String confirmWorkHour(Long userId,Long confirmId) {
        String msg = MessageUtils.message("work.hour.confirm.success");
        //校验用户是否有确认工时权限或者是否确认本人的周报工时
        try {
            if(workHourWeekMapper.checkWorkHourWeek(userId,confirmId)>0){
                workHourWeekMapper.confirmWorkHourWeek(userId,confirmId);
            }else{
                msg = MessageUtils.message("work.hour.confirm.noPermission");
            }
        } catch (Exception e) {
            logger.error("确认工时异常：" + e);
            msg = MessageUtils.message("work.hour.confirm.error") + e;
        }
        return msg;
    }

    @Override
    public void insertBatchWorkHour(List<WorkHour> workHours) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        for(WorkHour workHour : workHours){
            workHour.setUserId(loginUser.getUser().getUserId());
            workHour.setStatus(WorkHourConstants.WORK_HOUR_OK);
            workHour.setWorkStatus(WorkHourConstants.WORK_HOUR_SEND_OK);
            workHour.setCreateTime(DateUtils.getNowDate());
            workHour.setUpdateTime(DateUtils.getNowDate());
        }
        workHourMapper.insertBatchWorkHour(workHours);
    }

    @Override
    public List<WorkHour> copyLstWeekWorkHour() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        return workHourMapper.copyLstWeekWorkHour(loginUser.getUser().getUserId());
    }

}
